4. Visualização Geoespacial¶

A visualização de dados geoespaciais é uma ferramenta poderosa para comunicar padrões, tendências e insights espaciais. Com ela, cientistas de dados podem entender não apenas o "quê", mas o "onde".

Nesta seção, vamos explorar técnicas práticas para gerar mapas interativos com base em dados espaciais usando folium e geopandas.

4.1 Mapa de Pontos¶

Mapas de pontos são usados para mostrar onde eventos ou objetos estão localizados, como imóveis, sensores ou estabelecimentos comerciais.

In [19]:
import pandas as pd
import folium

# Carregar dados geocodificados de imóveis (já com latitude/longitude)
df = pd.read_csv("datasets/Singapore/geocodificados.csv")
df = df.dropna(subset=["latitude", "longitude"])

# Mapa básico centrado em Singapura
m = folium.Map(location=[1.3521, 103.8198], zoom_start=12, )

# Adicionar marcadores para cada imóvel
for _, row in df.iterrows():
    folium.CircleMarker(
        location=(row["latitude"], row["longitude"]),
        radius=5,
        color="blue",
        fill=True,
        fill_opacity=0.6,
        popup=f"${row['resale_price']:.0f}"
    ).add_to(m)

m
Out[19]:
Make this Notebook Trusted to load map: File -> Trust Notebook

4.2 Mapa Coropléticos

In [20]:
import geopandas as gpd
import folium
import pandas as pd

# Carregar dados dos imóveis
df = pd.read_csv("datasets/Singapore/geocodificados.csv")
df["preco_m2"] = df["resale_price"] / df["floor_area_sqm"]

# Padronizar nomes dos bairros
df["town_clean"] = df["town"].str.lower().str.strip()

# Calcular média de preço por região padronizada
media_por_town = df.groupby("town_clean")["preco_m2"].mean().reset_index()

# Carregar GeoJSON dos bairros
gdf_towns = gpd.read_file("datasets/Singapore/district_and_planning_area.geojson")
gdf_towns["planning_area_clean"] = gdf_towns["planning_area"].str.lower().str.strip()

# Merge com nomes padronizados
gdf_towns = gdf_towns.merge(media_por_town, left_on="planning_area_clean", right_on="town_clean")

# Criar mapa coroplético
m_choro = folium.Map(location=[1.3521, 103.8198], zoom_start=12)
folium.Choropleth(
    geo_data="datasets/Singapore/district_and_planning_area.geojson",
    data=gdf_towns,
    columns=["planning_area", "preco_m2"],
    key_on="feature.properties.planning_area",
    fill_color="YlGnBu",
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name="Preço médio por m² (SGD)"
).add_to(m_choro)

m_choro
Out[20]:
Make this Notebook Trusted to load map: File -> Trust Notebook

4.2 Mapa com Classificação por Preço/m²¶

Vamos classificar os imóveis como "Barato", "Médio" ou "Caro" com base nos quartis do preço por metro quadrado (preço/m²), e usar cores diferentes para cada categoria.

In [21]:
# Calcular preço por metro quadrado
df["preco_m2"] = df["resale_price"] / df["floor_area_sqm"]

# Definir os quartis
q1 = df["preco_m2"].quantile(0.25)
q3 = df["preco_m2"].quantile(0.75)

# Função para classificar
def classificar_preco(valor):
    if valor < q1:
        return "Barato"
    elif valor < q3:
        return "Médio"
    else:
        return "Caro"

# Aplicar classificação e definir cores
df["categoria"] = df["preco_m2"].apply(classificar_preco)
cores = {"Barato": "green", "Médio": "orange", "Caro": "red"}

# Mapa com cores por categoria
m2 = folium.Map(location=[1.3521, 103.8198], zoom_start=12)
df = df.dropna()
for _, row in df.iterrows():
    folium.CircleMarker(
        location=(row["latitude"], row["longitude"]),
        radius=5,
        color=cores[row["categoria"]],
        fill=True,
        fill_opacity=0.75,
        popup=f"{row['categoria']} - ${row['preco_m2']:.0f}/m²"
    ).add_to(m2)

m2
Out[21]:
Make this Notebook Trusted to load map: File -> Trust Notebook

4.3 Heatmap¶

Mapas de calor são úteis para visualizar a concentração de eventos ou objetos em uma área. Neste exemplo, vamos criar um heatmap para mostrar a densidade de imóveis.

In [22]:
from folium.plugins import HeatMap

# Mapa de calor da densidade de imóveis
m3 = folium.Map(location=[1.3521, 103.8198], zoom_start=12)
HeatMap(data=df[["latitude", "longitude"]]).add_to(m3)
m3
Out[22]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [23]:
import pandas as pd
import folium
from folium.plugins import MarkerCluster

# Carregar os dados
df = pd.read_csv("datasets/Singapore/geocodificados.csv")

# Remover registros com coordenadas ausentes
df = df.dropna(subset=["latitude", "longitude"])

# Criar mapa base
m = folium.Map(location=[1.3521, 103.8198], zoom_start=12)

# Criar o cluster de marcadores
marker_cluster = MarkerCluster().add_to(m)

# Adicionar os pontos ao cluster
for _, row in df.iterrows():
    popup_text = f"""
    <b>Preço:</b> ${row['resale_price']:,.0f}<br>
    <b>Área:</b> {row['floor_area_sqm']} m²<br>
    <b>Tipo:</b> {row['flat_type']}<br>
    <b>Bairro:</b> {row['town']}
    """
    folium.Marker(
        location=(row["latitude"], row["longitude"]),
        popup=folium.Popup(popup_text, max_width=250)
    ).add_to(marker_cluster)

# Exibir o mapa
m
Out[23]:
Make this Notebook Trusted to load map: File -> Trust Notebook

visualizacao temporal com mapa

In [24]:
from folium.plugins import TimestampedGeoJson
import folium
import pandas as pd

# Carregar os dados
df = pd.read_csv("datasets/Singapore/geocodificados.csv")

# Corrigir o tipo de dado e criar timestamp por lease
df["lease_date"] = pd.to_datetime(df["lease_commence_date"].astype(int).astype(str) + "-01-01")
df["timestamp"] = df["lease_date"].dt.strftime("%Y-%m-%d")

# Construir GeoJSON
features = []
for _, row in df.dropna(subset=["latitude", "longitude"]).iterrows():
    feature = {
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [row["longitude"], row["latitude"]],
        },
        "properties": {
            "time": row["timestamp"],
            "popup": f"{row['town']}<br>Lease: {row['lease_commence_date']}<br>Preço: ${row['resale_price']:,.0f}",
            "icon": "circle",
            "iconstyle": {
                "fillColor": "blue",
                "fillOpacity": 0.6,
                "stroke": "true",
                "radius": 5
            },
        },
    }
    features.append(feature)

geojson = {
    "type": "FeatureCollection",
    "features": features
}

# Criar mapa
m_time = folium.Map(location=[1.3521, 103.8198], zoom_start=12)
TimestampedGeoJson(geojson, period="P1Y", add_last_point=True).add_to(m_time)
m_time
Out[24]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [25]:
from folium.plugins import HeatMapWithTime
import pandas as pd
import folium

# Carregar e preparar os dados
df = pd.read_csv("datasets/Singapore/geocodificados.csv")
df = df.dropna(subset=["latitude", "longitude", "lease_commence_date"])
df["lease_commence_date"] = df["lease_commence_date"].astype(int)

# Agrupar os imóveis por ano (ex: de 1980 a 2020)
anos = sorted(df["lease_commence_date"].unique())
heat_data = []

for ano in anos:
    subset = df[df["lease_commence_date"] == ano]
    pontos = subset[["latitude", "longitude"]].values.tolist()
    heat_data.append(pontos)

# Criar o mapa
m_heat = folium.Map(location=[1.3521, 103.8198], zoom_start=12)

# Adicionar heatmap com animação temporal
HeatMapWithTime(
    heat_data,
    index=anos,
    auto_play=True,
    radius=10,
    max_opacity=0.8
).add_to(m_heat)
# Garantir que o campo esteja como inteiro
df["lease_commence_date"] = df["lease_commence_date"].astype(int)

# Contar lançamentos por ano
contagem = df["lease_commence_date"].value_counts().sort_index()

# Mostrar o ano com mais lançamentos
ano_top = contagem.idxmax()
qtd_top = contagem.max()

print(f"O ano com mais lançamentos foi {ano_top}, com {qtd_top} imóveis.")
m_heat
O ano com mais lançamentos foi 1985, com 92 imóveis.
Out[25]:
Make this Notebook Trusted to load map: File -> Trust Notebook